"""
FastAPI application for DeepMedical.
"""

import json
import logging
import os
import uuid
from typing import Dict, List, Any, Optional, Union

from Demos.win32ts_logoff_disconnected import username
from fastapi import FastAPI, HTTPException, Request, Depends, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi import Query

from poetry.console.commands import self
from pydantic import BaseModel, Field
from sse_starlette.sse import EventSourceResponse
import asyncio
from typing import AsyncGenerator, Dict, List, Any
from sqlalchemy.orm import Session
from sqlalchemy import or_


from src.graph import build_graph
from src.config import TEAM_MEMBERS, TEAM_MEMBER_CONFIGRATIONS, BROWSER_HISTORY_DIR
from src.service.workflow_service import run_agent_workflow
from src.service.markdown_service import MarkdownService
from src.service.session_service import SessionService
from src.service.chat_service import ChatService

from src.models.session import Session as SessionModel, Message as MessageModel

from src.database.db import engine, get_db, SessionLocal
from src.api import endpoints as api_endpoints
from src.auth import endpoints as auth_endpoints
from src.models.user_models import Base
from starlette.responses import JSONResponse, StreamingResponse
import re
from urllib.parse import quote
from datetime import datetime
# Configure logging
logger = logging.getLogger(__name__)

# Create FastAPI app
app = FastAPI(
    docs_url="/docs",
    swagger_js_url="/static/swagger-ui-bundle.js",
    swagger_css_url="/static/swagger-ui.css",
    title="DeepMedical API",
    description="API for DeepMedical LangGraph-based agent workflow",
    version="0.1.0",
)
markdown_service = MarkdownService()
session_service = SessionService()
chat_service_instance = ChatService()

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # Allows all origins
    allow_credentials=True,
    allow_methods=["*"],  # Allows all methods
    allow_headers=["*"],# Allows all headers
    expose_headers=["Content-Disposition"]  # 允许前端读取下载头

)

# Create the graph
graph = build_graph()

# Include routers
app.include_router(api_endpoints.router, prefix="/api")
app.include_router(auth_endpoints.router,prefix="/api")

# Create database tables
Base.metadata.create_all(bind=engine)


class ContentItem(BaseModel):
    type: str = Field(..., description="The type of content (text, image, etc.)")
    text: Optional[str] = Field(None, description="The text content if type is 'text'")
    image_url: Optional[str] = Field(
        None, description="The image URL if type is 'image'"
    )


class ChatMessage(BaseModel):
    role: str = Field(
        ..., description="The role of the message sender (user or assistant)"
    )
    content: Union[str, List[ContentItem]] = Field(
        ...,
        description="The content of the message, either a string or a list of content items",
    )


class ChatRequest(BaseModel):
    messages: List[ChatMessage] = Field(..., description="The conversation history")
    debug: Optional[bool] = Field(False, description="Whether to enable debug logging")
    deep_thinking_mode: Optional[bool] = Field(
        False, description="Whether to enable deep thinking mode"
    )
    search_before_planning: Optional[bool] = Field(
        False, description="Whether to search before planning"
    )
    team_members: Optional[list] = Field(None, description="enabled team members")
    session_id: Optional[str] = Field(None, description="Session ID for continuing a conversation")
    username: Optional[str] = Field(None, description="User ID for session isolation")  # 修改为 Optional[int]
    mode: Optional[str] = Field("search", description="Chat mode: 'search' (workflow) or 'chat' (direct LLM)")
    

# 会话模型
class SessionResponse(BaseModel):
    id: str
    created_at: datetime
    updated_at: datetime

# 创建会话请求
class CreateSessionRequest(BaseModel):
    username: Optional[str] = None

# 会话历史响应
class SessionHistoryResponse(BaseModel):
    session_id: str
    messages: List[Dict[str, Any]]
    state: Optional[Dict[str, Any]] = None

@app.middleware("http")
async def db_session_middleware(request: Request, call_next):
    # 为每个请求创建数据库会话
    request.state.db = SessionLocal()
    response = None
    try:
        response = await call_next(request)
    finally:
        # 请求结束后关闭会话
        request.state.db.close()
    return response


@app.post("/api/chat/stream")
async def chat_endpoint(request: ChatRequest, req: Request):
    """
    Chat endpoint supporting dual mode: search (workflow) and chat (direct LLM).

    Args:
        request: The chat request containing messages and mode
        req: The FastAPI request object for connection state checking

    Returns:
        The streamed response as Server-Sent Events
    """
    db = None
    try:
        # 获取数据库会话
        db = SessionLocal()
        
        # 确定聊天模式 (默认为 "search")
        chat_mode = request.mode.lower() if request.mode else "search"
        logger.info(f"Chat endpoint called with mode: {chat_mode}")
        
        # 获取当前用户消息 (最后一条消息)
        if not request.messages or len(request.messages) == 0:
            raise HTTPException(status_code=400, detail="No messages provided")
            
        current_user_message = request.messages[-1]
        current_user_message_dict = {
            "role": current_user_message.role,
            "content": current_user_message.content
        }
        
        # 规范化用户消息内容
        user_content_for_db = current_user_message.content
        if not isinstance(user_content_for_db, str):
            # 如果内容是列表，转换为JSON字符串
            user_content_for_db = json.dumps(user_content_for_db, ensure_ascii=False)
        
        # 处理会话 ID
        current_session_id = request.session_id
        if not current_session_id:
            # 创建新会话
            new_session = await session_service.create_session(db, request.username)
            current_session_id = new_session.id
            logger.info(f"Created new session with ID: {current_session_id}")
        
        # 保存用户消息到会话
        await session_service.add_message(
            db, 
            session_id=current_session_id, 
            role=current_user_message_dict.get("role", "user"), 
            content=user_content_for_db, 
            message_type="text",
            username=request.username
        )
        
        # 根据模式选择处理方式
        async def event_generator() -> AsyncGenerator[Dict, None]:
            try:
                if chat_mode == "chat":
                    # 使用 ChatService 进行简单聊天
                    logger.info(f"Using chat mode for session: {current_session_id}")
                    async for event in chat_service_instance.run_simple_chat(
                        session_id=current_session_id,
                        user_input_message=current_user_message_dict,
                        username=request.username
                    ):
                        # 检查客户端是否仍然连接
                        if await req.is_disconnected():
                            logger.info("Client disconnected, stopping chat")
                            break
                            
                        event_type = event.get("event")
                        event_data = event.get("data", {})
                        
                        yield {
                            "event": event_type,
                            "data": json.dumps(event_data, ensure_ascii=False),
                        }
                else:
                    # 使用 WorkflowService 进行搜索模式
                    logger.info(f"Using search mode for session: {current_session_id}")
                    
                    # 转换消息格式
                    messages = []
                    for msg in request.messages:
                        message_dict = {"role": msg.role}
                        
                        # 处理字符串内容和内容项列表
                        if isinstance(msg.content, str):
                            message_dict["content"] = msg.content
                        else:
                            # 转换为工作流期望的格式
                            content_items = []
                            for item in msg.content:
                                if item.type == "text" and item.text:
                                    content_items.append({"type": "text", "text": item.text})
                                elif item.type == "image" and item.image_url:
                                    content_items.append(
                                        {"type": "image", "image_url": item.image_url}
                                    )
                            message_dict["content"] = content_items
                        
                        messages.append(message_dict)
                    
                    async for event in run_agent_workflow(
                        messages,
                        request.debug,
                        request.deep_thinking_mode,
                        request.search_before_planning,
                        request.team_members,
                        current_session_id,  # 传递session_id
                    ):
                        # 检查客户端是否仍然连接
                        if await req.is_disconnected():
                            logger.info("Client disconnected, stopping workflow")
                            break
                            
                        # 兼容两种事件格式
                        event_type = event.get("type") or event.get("event")
                        event_data = event.get("data", {})
                        
                        yield {
                            "event": event_type,
                            "data": json.dumps(event_data, ensure_ascii=False),
                        }
                
                # 在最后一个事件后返回session_id
                if current_session_id:
                    # 使用与其他事件相同的格式
                    logger.info(f"Yielding session_id event for session: {current_session_id}")
                    yield {
                        "event": "session_id",
                        "data": json.dumps({"session_id": current_session_id}, ensure_ascii=False),
                    }
                    
            except asyncio.CancelledError:
                logger.info("Stream processing cancelled")
                raise
            except Exception as e_inner:
                logger.error(f"Error in event generator: {str(e_inner)}")
                error_event = {
                    "event": "error",
                    "data": json.dumps({"error": str(e_inner)}, ensure_ascii=False),
                }
                yield error_event

        return EventSourceResponse(
            event_generator(),
            media_type="text/event-stream",
            sep="\n",
        )
    except Exception as e:
        logger.error(f"Error in chat endpoint: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/api/browser_history/{filename}")
async def get_browser_history_file(filename: str):
    """
    Get a specific browser history GIF file.

    Args:
        filename: The filename of the GIF to retrieve

    Returns:
        The GIF file
    """
    try:
        file_path = os.path.join(BROWSER_HISTORY_DIR, filename)
        if not os.path.exists(file_path) or not filename.endswith(".gif"):
            raise HTTPException(status_code=404, detail="File not found")

        return FileResponse(file_path, media_type="image/gif", filename=filename)
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error retrieving browser history file: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/api/team_members")
async def get_team_members():
    """
    Get the configuration of all team members.

    Returns:
        dict: A dictionary containing team member configurations
    """
    try:
        return {"team_members": TEAM_MEMBER_CONFIGRATIONS}
    except Exception as e:
        logger.error(f"Error getting team members: {e}")
        raise HTTPException(status_code=500, detail=str(e))

# 需要身份验证的 security 依赖
secured_security = HTTPBearer()

# 允许匿名访问的 security 依赖
def optional_security() -> HTTPAuthorizationCredentials:
    try:
        return Depends(secured_security)
    except HTTPException as e:
        # 如果没有提供 token，则返回 None
        return None
# 创建新会话
# 初始化聊天会话
@app.post("/api/chat/initiate", response_model=dict)
async def initiate_chat(
        request: dict,
        req: Request,
        db: Session = Depends(get_db),
        token: Optional[HTTPAuthorizationCredentials] = Depends(optional_security)
):
    """初始化聊天会话，创建会话ID并处理第一条消息

    Args:
        request: 包含用户第一条消息和配置的请求
        req: FastAPI请求对象
        db: 数据库会话
        身份验证

    Returns:
        包含会话ID和初始响应的字典
    """
    try:
        # 从请求体中获取用户名
        username = request.get("username", None)
        authenticated = False
        # 如果提供了 token，则进行身份验证
        if token:
            # 在这里进行身份验证逻辑，例如验证 token 是否有效
            # 如果 token 无效，则抛出 HTTPException
            # 例如：
            # try:
            #     payload = jwt.decode(token.credentials, "your-secret-key", algorithms=["HS256"])
            #     username = payload.get("username")
            # except jwt.ExpiredSignatureError:
            #     raise HTTPException(status_code=401, detail="Token has expired")
            # except jwt.InvalidTokenError:
            #     raise HTTPException(status_code=401, detail="Invalid token")
            pass  # 替换为你的身份验证逻辑
        else:
            # 允许匿名访问
            logger.info("Anonymous access to /api/chat/initiate")
            username = f"anonymous_{uuid.uuid4()}"  # 为匿名用户生成一个唯一的 username
        # 创建新会话
        if not username:
            logger.info("创建匿名会话")
            # 使用UUID和IP组合，增加识别性
            client_ip = req.client.host if req.client else "unknown"
            username = f"anonymous_{uuid.uuid4()}_{client_ip.replace('.', '_')}"
        session = await SessionService.create_session(db, username=username)
        session_id = session.id
        username=username
        logger.info(f"Created new session: {session_id}")

        # 构造消息
        message = {
            "role": "user",
            "content": request.get("message", "")
        }

        # 保存第一条消息
        await SessionService.add_message(
            db,
            session_id=session_id,
            role=message["role"],
            content=message["content"],
            message_type="text",  # 显式指定消息类型为text
            username=username
        )

        return {
            "session_id": session_id,
            "username": username,
            "is_authenticated": authenticated,
            "initial_messages": [],  # 如果需要立即返回初始回复，可以在这里添加
            "status": "success"
        }
    except Exception as e:
        logger.error(f"Error initiating chat: {e}")
        raise HTTPException(status_code=500, detail=str(e))

# 获取会话历史
@app.get("/api/session/{session_id}/history", response_model=SessionHistoryResponse)
async def get_session_history(
    session_id: str, 
    req: Request,
    db: Session = Depends(get_db),
    x_username: Optional[str] = Header(None, alias="X-Username")
):
    """获取会话历史"""
    try:
        # 获取当前用户名
        username = x_username

        # 如果没有提供用户名头，尝试从请求中获取
        if not username:
            # 尝试从查询参数获取
            username = req.query_params.get("username")
            if username:
                logger.info(f"从查询参数获取用户名: {username}")

        # 如果仍无用户名，拒绝访问
        if not username:
            logger.warning("未提供用户名，拒绝访问")
            raise HTTPException(
                status_code=401,
                detail="需要提供用户名以验证身份"
            )

        logger.info(f"获取会话历史请求: ID={session_id}, 用户={username}")

        # 1. 获取会话并验证所有权
        session = await SessionService.get_session(db, session_id, username)
        if not session:
            logger.warning(f"会话不存在或用户不匹配: ID={session_id}, 请求用户={username}")
            raise HTTPException(
                status_code=404,
                detail=f"会话不存在或您无权访问"
            )

        # 2. 获取会话消息（不需要用户名过滤，因为会话已验证）
        messages = await SessionService.get_session_messages(db, session_id)

        # 3. 格式化消息 - 从会话获取用户名
        formatted_messages = []
        for msg in messages:
            try:
                # 尝试解析JSON内容
                content = json.loads(msg.content) if isinstance(msg.content, str) else msg.content
            except:
                content = msg.content

            formatted_messages.append({
                "id": msg.id,
                "role": msg.role,
                "type": msg.type,
                "content": content,
                "created_at": msg.created_at.isoformat(),
                # 使用会话的用户名，因为消息没有username字段
                "username": session.username
            })

        # 4. 确保state字段存在
        state = session.state if session.state else {}

        logger.info(f"为用户 {username} 获取会话 {session_id} 历史: {len(formatted_messages)} 条消息")

        return {
            "session_id": session_id,
            "username": username,
            "messages": formatted_messages,
            "state": state
        }

    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"获取会话历史失败: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail="内部服务器错误")


# 历史会话列表
class SessionListItem(BaseModel):
    session_id: str
    created_at: datetime
    updated_at: datetime
    preview: Optional[str] = None



# 获取历史会话列表
@app.get("/api/session/list", response_model=List[SessionListItem])
async def list_user_sessions(
    req: Request,
    db: Session = Depends(get_db),
    x_username: Optional[str] = Header(None, alias="X-Username")
):
    try:
        username = x_username or req.query_params.get("username")
        if not username:
            raise HTTPException(status_code=401, detail="需要提供用户名")

        logger.info(f"获取用户 {username} 的会话列表")

        sessions = db.query(SessionModel).filter(
            SessionModel.username == username
        ).order_by(SessionModel.updated_at.desc()).all()

        result = []

        for s in sessions:
            # 获取第一条消息
            first_msg = db.query(MessageModel).filter(
                MessageModel.session_id == s.id
            ).order_by(MessageModel.created_at.asc()).first()

            preview = None
            if first_msg:
                try:
                    parsed = json.loads(first_msg.content)
                    if isinstance(parsed, dict) and "text" in parsed:
                        preview = parsed["text"]
                    else:
                        preview = str(parsed)
                except:
                    preview = first_msg.content

            result.append({
                "session_id": s.id,
                "created_at": s.created_at,
                "updated_at": s.updated_at,
                "preview": preview
            })

        return result

    except Exception as e:
        logger.error(f"获取用户会话列表失败: {e}")
        raise HTTPException(status_code=500, detail="获取用户会话列表失败")


# 会话搜索结果模型
class SessionSearchResult(BaseModel):
    session_id: str
    created_at: datetime
    updated_at: datetime
    matched_message: str


# 获取搜索结果
@app.get("/api/session/search", response_model=List[SessionSearchResult])
async def search_user_sessions(
    keyword: str = Query(..., description="要搜索的关键词"),
    req: Request = None,
    db: Session = Depends(get_db),
    x_username: Optional[str] = Header(None, alias="X-Username")
):
    try:
        username = x_username or req.query_params.get("username")
        if not username:
            raise HTTPException(status_code=401, detail="需要提供用户名")

        if not keyword.strip():
            raise HTTPException(status_code=400, detail="关键词不能为空")

        logger.info(f"用户 {username} 正在搜索聊天记录，关键词: {keyword}")

        # 查找该用户所有会话
        sessions = db.query(SessionModel).filter(
            SessionModel.username == username
        ).all()

        result = []

        for session in sessions:
            # 查找该会话中包含关键词的消息
            matched_msg = db.query(MessageModel).filter(
                MessageModel.session_id == session.id,
                MessageModel.content.ilike(f"%{keyword}%")
            ).order_by(MessageModel.created_at.asc()).first()

            if matched_msg:
                # 尝试解析消息内容
                try:
                    content = json.loads(matched_msg.content)
                    if isinstance(content, dict) and "text" in content:
                        content_text = content["text"]
                    else:
                        content_text = str(content)
                except:
                    content_text = matched_msg.content

                result.append({
                    "session_id": session.id,
                    "created_at": session.created_at,
                    "updated_at": session.updated_at,
                    "matched_message": content_text[:100]  # 限制展示长度
                })

        return result

    except Exception as e:
        logger.error(f"搜索聊天记录失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail="搜索聊天记录失败")



# 删除会话节点
@app.delete("/api/session/{session_id}", response_model=dict)
async def delete_session(
    session_id: str,
    req: Request,
    db: Session = Depends(get_db),
    x_username: Optional[str] = Header(None, alias="X-Username")
):
    """删除会话及其所有消息
    
    Args:
        session_id: 要删除的会话ID
        req: FastAPI请求对象
        db: 数据库会话
        x_username: 用户身份验证头
        
    Returns:
        包含操作结果的字典
    """
    try:
        # 获取当前用户名
        username = x_username or req.query_params.get("username")
        if not username:
            logger.warning("未提供用户名，拒绝删除会话请求")
            raise HTTPException(
                status_code=401,
                detail="需要提供用户名以验证身份"
            )

        logger.info(f"用户 {username} 请求删除会话: ID={session_id}")

        # 调用服务删除会话
        success = await SessionService.delete_session(db, session_id, username)
        if not success:
            raise HTTPException(
                status_code=404,
                detail=f"会话不存在或您无权删除"
            )

        return {
            "status": "success",
            "message": "会话删除成功",
            "deleted_session_id": session_id
        }

    except HTTPException as he:
        raise he
    except Exception as e:
        logger.error(f"删除会话失败: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail="删除会话失败")


@app.post("/api/save-as-markdown")
async def save_as_markdown(article_data: dict):
    """保存文章为Markdown文件"""
    try:
        # 参数校验
        required_fields = ["filename", "content"]
        for field in required_fields:
            if field not in article_data:
                raise HTTPException(400, detail=f"Missing required field: {field}")

        # 调用服务保存
        result = markdown_service.save_article_as_markdown(article_data)

        if result["status"] == "error":
            raise HTTPException(400, detail=result)

        return JSONResponse(content=result)

    except HTTPException as he:
        raise
    except Exception as e:
        raise HTTPException(500, detail=str(e))


# 修改下载接口为查询参数形式
@app.get("/api/download/markdown")
async def download_markdown(filename: str):
    try:
        # 严格匹配存储的文件名格式
        if not re.match(r"^[\w\u4e00-\u9fa5\-_.]+\.md$", filename):
            raise HTTPException(400, "无效文件名格式")

        filepath = markdown_service.output_dir / filename

        # 增强存在性检查
        if not filepath.is_file():
            raise HTTPException(404, detail={
                "error_code": "FILE_NOT_FOUND",
                "suggested": "使用/api/list获取有效文件列表"
            })

        # 记录下载日志
        logger.info(f"下载文件: {filename} 大小: {filepath.stat().st_size}字节")
        return FileResponse(filepath)

    except HTTPException as he:
        raise
    except ValueError as ve:
        raise HTTPException(400, detail=str(ve))
    except Exception as e:
        raise HTTPException(500, detail=str(e))